﻿using Framework.EncryptionDecryptionService.Achieve;
using Framework.EncryptionDecryptionService.Attributes;
using Framework.EncryptionDecryptionService.Enums;
using Framework.EncryptionDecryptionService.Helpers;
using Framework.EncryptionDecryptionService.Inteface;
using Framework.EncryptionDecryptionService.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Framework.EncryptionDecryptionService.Middleware
{
    /// <summary>
    /// 加解密中间件
    /// </summary>
    public class EncryptionDecryptionMiddleware
    {
        private readonly RequestDelegate _next;

        /// <summary>
        /// 加密服务
        /// </summary>
        private readonly IEncryptionDecryptionService _encryptionService;

        /// <summary>
        /// 加解密中间件
        /// </summary>
        /// <param name="next"></param>
        /// <param name="encryptionService"></param>
        public EncryptionDecryptionMiddleware(RequestDelegate next, IEncryptionDecryptionService encryptionService)
        {
            _next = next;
            _encryptionService = encryptionService;
        }

        /// <summary>
        /// 入参解密 | 返回值加密
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            var endpoint = context.GetEndpoint();
            // 检查endpoint是否存在
            if (endpoint == null)
            {
                await _next(context);
                return;
            }

            //获取API的 NonEncryptedAttribute 特性
            var encryptionAttribute = endpoint.Metadata.GetMetadata<NonEncryptedAttribute>();
            //API的 NonEncryptedAttribute 特性为空时获取控制器的 NonEncryptedAttribute 特性
            encryptionAttribute ??= endpoint.Metadata.GetMetadata<ControllerActionDescriptor>()?.ControllerTypeInfo.GetCustomAttribute<NonEncryptedAttribute>();

            // 不需要加密的情况
            if (encryptionAttribute != null && encryptionAttribute.EncryptionTypes == EncryptionType.None)
            {
                await _next(context);
                return;
            }

            //默认全加密
            encryptionAttribute ??= new NonEncryptedAttribute(EncryptionType.Full);

            #region -- 获取请求头
            if (!context.Request.Headers.TryGetValue("SecretKey", out var aesSecretKey))
            {
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                await context.Response.WriteAsync("非法请求：无秘钥信息");
                return;
            }
            #endregion

            #region -- 使用RSA算法解密AES秘钥
            SecretKeyModel aesSecretKeyModel;
            try
            {
                aesSecretKeyModel = _encryptionService.DecryptionService(EncryptionAlgorithm.RSA).Decrypt<SecretKeyModel>(aesSecretKey);

                if (aesSecretKeyModel == null)
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    await context.Response.WriteAsync("非法请求：无效秘钥");
                    return;
                }
                context.Items["SecretKey"] = aesSecretKeyModel.ConvertToString();
            }
            catch (Exception ex)
            {
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                await context.Response.WriteAsync($"秘钥解密失败：{ex.Message}");
                return;
            }
            #endregion

            // 请求解密
            if (encryptionAttribute.EncryptionTypes == EncryptionType.Full || encryptionAttribute.EncryptionTypes == EncryptionType.Request)
            {
                try
                {
                    context.Request.EnableBuffering(); // 允许多次读取body
                    using var reader = new StreamReader(context.Request.Body, leaveOpen: true);
                    var strBody = await reader.ReadToEndAsync();
                    context.Request.Body.Position = 0;

                    if (string.IsNullOrWhiteSpace(strBody))
                    {
                        context.Response.StatusCode = StatusCodes.Status400BadRequest;
                        await context.Response.WriteAsync("非法请求：入参为空");
                        return;
                    }

                    var serviceAes = new DecryptionServiceAes(new SecretKeyServiceAes(aesSecretKeyModel));
                    var decryptedBody = serviceAes.Decrypt(strBody);

                    // 替换请求体
                    var bytes = Encoding.UTF8.GetBytes(decryptedBody);
                    context.Request.Body = new MemoryStream(bytes);
                    context.Request.ContentLength = bytes.Length;
                }
                catch (Exception ex)
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    await context.Response.WriteAsync($"请求解密失败：{ex.Message}");
                    return;
                }
            }

            // 捕获原始响应流
            var originalBody = context.Response.Body;
            try
            {
                using var memoryStream = new MemoryStream();
                context.Response.Body = memoryStream;

                await _next(context);

                // 处理响应加密
                if (encryptionAttribute.EncryptionTypes == EncryptionType.Full || encryptionAttribute.EncryptionTypes == EncryptionType.Response)
                {
                    memoryStream.Position = 0;
                    //异步读取返回值
                    var responseContent = await new StreamReader(memoryStream).ReadToEndAsync();

                    //构造AES加密服务
                    var serviceAes = new EncryptionServiceAes(new SecretKeyServiceAes(aesSecretKeyModel));
                    //使用AES服务加密返回值
                    var encryptedContent = serviceAes.Encrypt(responseContent).ToBase64String();

                    //将加密后的字符串转换为流
                    using MemoryStream memory = new MemoryStream();
                    await using StreamWriter streamWriter = new StreamWriter(memory);
                    await streamWriter.WriteAsync(encryptedContent);
                    await streamWriter.FlushAsync();

                    //将加密内容复制到原始响应流中
                    memory.Position = 0;
                    await memory.CopyToAsync(originalBody);
                }
                else
                {
                    //返回值不需要加密，将返回值重新赋值给原始响应流
                    memoryStream.Position = 0;
                    await memoryStream.CopyToAsync(originalBody); 
                }
            }
            catch (Exception e)
            {
                using MemoryStream memoryStream = new MemoryStream();
                await using StreamWriter stream = new StreamWriter(memoryStream);
                await stream.WriteAsync($"服务出现未知异常");
                Console.WriteLine($"返回值加密出现未知异常：{e.Message}，异常明细：{e}");
                await memoryStream.CopyToAsync(originalBody);

            }
            finally
            {
                context.Response.Body = originalBody;
            }
        }
    }
}
